package com.echat.elocation.util;

import com.echat.elocation.protocol.entity.PackageMsg;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * JT808协议转义工具类
 *
 * @author lihao
 *
 * @date 2018/7/16 13:52
 */
public class ProtocolUtils {

    /**
     * 发送消息时转义
     *
     * @param bs    要转义的字节数组
     * @param start 起始索引
     * @param end   结束索引
     * @return 转义后的字节数组
     * @throws Exception
     */
    public static byte[] doEscape4Send(byte[] bs, int start, int end) throws Exception {
        if (start < 0 || end > bs.length) {
            throw new ArrayIndexOutOfBoundsException("doEscape4Receive error : index out of bounds(start=" + start
                    + ",end=" + end + ",bytes length=" + bs.length + ")");
        }
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            for (int i = 0; i < start; i++) {
                baos.write(bs[i]);
            }
            for (int i = start; i < end; i++) {
                if (bs[i] == 0x7e) {
                    baos.write(0x7d);
                    baos.write(0x02);
                } else if (bs[i] == 0x7d) {
                    baos.write(0x7d);
                    baos.write(0x01);
                } else {
                    baos.write(bs[i]);
                }
            }
            for (int i = end; i < bs.length; i++) {
                baos.write(bs[i]);
            }
            return baos.toByteArray();
        } finally {
            if (baos != null) {
                baos.close();
            }
        }
    }

    /**
     * 接收消息时转义
     * 0x7d01 <====> 0x7d
     * 0x7d02 <====> 0x7e
     *
     * @param bs    要转义还原的字节数组
     * @param start 起始索引
     * @param end   结束索引
     * @return 转义还原后的字节数组
     * @throws Exception
     */
    public static byte[] doEscape4Receive(byte[] bs, int start, int end) throws Exception {
        if (start < 0 || end > bs.length) {
            throw new ArrayIndexOutOfBoundsException("doEscape4Receive error : index out of bounds(start=" + start
                    + ",end=" + end + ",bytes length=" + bs.length + ")");
        }
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            for (int i = 0; i < start; i++) {
                baos.write(bs[i]);
            }
            for (int i = start; i < end - 1; i++) {
                if (bs[i] == 0x7d && bs[i + 1] == 0x01) {
                    baos.write(bs[0x7d]);
                    i++;
                } else if (bs[i] == 0x7d && bs[i + 1] == 0x02) {
                    baos.write(0x7e);
                    i++;
                } else {
                    baos.write(bs[i]);
                }
            }
            for (int i = end - 1; i < bs.length; i++) {
                baos.write(bs[i]);
            }
            return baos.toByteArray();
        } finally {
            if (baos != null) {
                baos.close();
            }
        }
    }

    public static int generateMsgBodyProps(int msgBodyLength){
        return generateMsgBodyProps(msgBodyLength, 0, false, 0);
    }

    /**
     * 消息体属性
     *
     * @param msgBodyLength  消息体长度
     * @param encryptionType 加密类型
     * @param hasSubPackage  是否分包
     * @param reservedBit    保留位
     * @return 形成后的消息体属性
     */
    public static int generateMsgBodyProps(int msgBodyLength, int encryptionType,
                                           boolean hasSubPackage, int reservedBit) {
        // [ 0-9 ] 0000,0011,1111,1111(3FF)(消息体长度)
        // [10-12] 0001,1100,0000,0000(1C00)(加密类型)
        // [ 13_ ] 0010,0000,0000,0000(2000)(是否有子包)
        // [14-15] 1100,0000,0000,0000(C000)(保留位)
        int maxLength = 1024;
        if (msgBodyLength >= maxLength) {
            Logger.d("ProtocolUtils", "The max value of msgLen is 1023, but {} ." + msgBodyLength);
        }
        int subPkg = hasSubPackage ? 1 : 0;
        int ret = (msgBodyLength & 0x3FF) | ((encryptionType << 10) & 0x1C00) | ((subPkg << 13) & 0x2000)
                | ((reservedBit << 14) & 0xC000);
        return ret & 0xFFFF;
    }

    public static byte[] generateMsgHeader(PackageMsg.MsgHeader header) throws IOException {
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            baos.write(BitOperation.integerTo2Bytes(header.getMsgId()));
            baos.write(BitOperation.integerTo2Bytes(header.getMsgBodyProps()));
            baos.write(BcdOperation.string2Bcd(HexStringUtils.preFillZero(header.getPhoneNumber(), 12)));
            baos.write(BitOperation.integerTo2Bytes(header.getFlowId()));
            return baos.toByteArray();
        } finally {
            if (baos != null) {
                baos.close();
            }
        }
    }

    /**
     * 获取检验码
     * 从消息头开始，同后一个字节开始异或，直到检验码的前一个字节，占用一个字节
     *
     * @param bs    消息字节数组
     * @param start 起始索引
     * @param end   结束索引
     * @return 检验码
     */
    public static byte getCheckCode(byte[] bs, int start, int end) {
        if (start < 0 || end > bs.length) {
            throw new ArrayIndexOutOfBoundsException("doEscape4Receive error : index out of bounds(start=" + start
                    + ",end=" + end + ",bytes length=" + bs.length + ")");
        }
        byte cs = 0;
        for (int i = start; i < end; i++) {
            cs ^= bs[i];
        }
        return cs;
    }

    public static byte getCheckCode(byte[] bs){
        byte cs = 0;
        for (byte b : bs) {
            cs ^= b;
        }
        return cs;
    }
}
